#ifndef _GLUVARTYPE_HPP
#define _GLUVARTYPE_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES Utilities
 * ------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Shader variable type.
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "gluShaderUtil.hpp"

#include <vector>
#include <string>
#include <ostream>

namespace glu
{

class StructType;

/*--------------------------------------------------------------------*//*!
 * \brief Shader variable type.
 *
 * Variable type represents data type. No storage qualifiers are supported
 * since they are associated to a declaration, not to the variable type.
 *
 * \note Structs are handled using struct pointers since it is often desirable
 *		 to maintain unique list of struct declarations.
 *//*--------------------------------------------------------------------*/
class VarType
{
public:
						VarType			(void);
						VarType			(const VarType& other);

						VarType			(DataType basicType, Precision precision);		//!< Basic type constructor.
						VarType			(const VarType& elementType, int arraySize);	//!< Array type constructor.
	explicit			VarType			(const StructType* structPtr);					//!< Struct type constructor.
						~VarType		(void);

	bool				isBasicType		(void) const	{ return m_type == VARTYPE_BASIC;	}
	bool				isArrayType		(void) const	{ return m_type == VARTYPE_ARRAY;	}
	bool				isStructType	(void) const	{ return m_type == VARTYPE_STRUCT;	}

	DataType			getBasicType	(void) const	{ DE_ASSERT(isBasicType()); return m_data.basic.type;			}
	Precision			getPrecision	(void) const	{ DE_ASSERT(isBasicType()); return m_data.basic.precision;		}

	const VarType&		getElementType	(void) const	{ DE_ASSERT(isArrayType()); return *m_data.array.elementType;	}
	int					getArraySize	(void) const	{ DE_ASSERT(isArrayType()); return m_data.array.size;			}

	const StructType*	getStructPtr	(void) const	{ DE_ASSERT(isStructType()); return m_data.structPtr;			}

	int					getScalarSize	(void) const;

	VarType&			operator=		(const VarType& other);

	bool				operator==		(const VarType& other) const;
	bool				operator!=		(const VarType& other) const;

	enum
	{
		UNSIZED_ARRAY = -1 //!< Array length for unsized arrays.
	};

private:
	enum Type
	{
		VARTYPE_BASIC,
		VARTYPE_ARRAY,
		VARTYPE_STRUCT,

		VARTYPE_LAST
	};

	Type				m_type;
	union Data
	{
		// TYPE_BASIC
		struct
		{
			DataType		type;
			Precision		precision;
		} basic;

		// TYPE_ARRAY
		struct
		{
			VarType*		elementType;
			int				size;
		} array;

		// TYPE_STRUCT
		const StructType*	structPtr;

		Data (void)
		{
			array.elementType	= DE_NULL;
			array.size			= 0;
		};
	} m_data;
} DE_WARN_UNUSED_TYPE;

template <typename T>
inline VarType varTypeOf (Precision prec = PRECISION_LAST) { return VarType(dataTypeOf<T>(), prec); }

class StructMember
{
public:
						StructMember	(const char* name, const VarType& type) : m_name(name), m_type(type) {}
						StructMember	(void) {}

	const char*			getName			(void) const { return m_name.c_str();	}
	const VarType&		getType			(void) const { return m_type;			}

	bool				operator==		(const StructMember& other) const;
	bool				operator!=		(const StructMember& other) const;

private:
	std::string			m_name;
	VarType				m_type;
} DE_WARN_UNUSED_TYPE;

class StructType
{
public:
	typedef std::vector<StructMember>::iterator			Iterator;
	typedef std::vector<StructMember>::const_iterator	ConstIterator;

								StructType		(const char* typeName) : m_typeName(typeName) {}
								~StructType		(void) {}

	bool						hasTypeName		(void) const	{ return !m_typeName.empty();	}
	const char*					getTypeName		(void) const	{ return hasTypeName() ? m_typeName.c_str() : DE_NULL; }

	void						addMember		(const char* name, const VarType& type);

	int							getNumMembers	(void) const	{ return (int)m_members.size();	}
	const StructMember&			getMember		(int ndx) const	{ return m_members[ndx];		}

	inline Iterator				begin			(void)			{ return m_members.begin();		}
	inline ConstIterator		begin			(void) const	{ return m_members.begin();		}
	inline Iterator				end				(void)			{ return m_members.end();		}
	inline ConstIterator		end				(void) const	{ return m_members.end();		}

	bool						operator==		(const StructType& other) const;
	bool						operator!=		(const StructType& other) const;

private:
	std::string					m_typeName;
	std::vector<StructMember>	m_members;
} DE_WARN_UNUSED_TYPE;

enum Storage
{
	STORAGE_IN = 0,
	STORAGE_OUT,
	STORAGE_CONST,
	STORAGE_UNIFORM,
	STORAGE_BUFFER,
	STORAGE_PATCH_IN,
	STORAGE_PATCH_OUT,
	STORAGE_LAST
};

const char* getStorageName (Storage storage);

enum Interpolation
{
	INTERPOLATION_SMOOTH = 0,
	INTERPOLATION_FLAT,
	INTERPOLATION_CENTROID,
	INTERPOLATION_LAST
};

const char* getInterpolationName (Interpolation interpolation);

enum FormatLayout
{
	FORMATLAYOUT_RGBA32F = 0,
	FORMATLAYOUT_RGBA16F,
	FORMATLAYOUT_R32F,
	FORMATLAYOUT_RGBA8,
	FORMATLAYOUT_RGBA8_SNORM,

	FORMATLAYOUT_RGBA32I,
	FORMATLAYOUT_RGBA16I,
	FORMATLAYOUT_RGBA8I,
	FORMATLAYOUT_R32I,

	FORMATLAYOUT_RGBA32UI,
	FORMATLAYOUT_RGBA16UI,
	FORMATLAYOUT_RGBA8UI,
	FORMATLAYOUT_R32UI,

	FORMATLAYOUT_LAST
};

const char* getFormatLayoutName (FormatLayout layout);

enum MemoryAccessQualifier
{
	MEMORYACCESSQUALIFIER_COHERENT_BIT	= 0x01,
	MEMORYACCESSQUALIFIER_VOLATILE_BIT	= 0x02,
	MEMORYACCESSQUALIFIER_RESTRICT_BIT	= 0x04,
	MEMORYACCESSQUALIFIER_READONLY_BIT	= 0x08,
	MEMORYACCESSQUALIFIER_WRITEONLY_BIT	= 0x10,

	MEMORYACCESSQUALIFIER_MASK = (MEMORYACCESSQUALIFIER_WRITEONLY_BIT << 1) - 1
};

const char* getMemoryAccessQualifierName (MemoryAccessQualifier qualifier);

enum MatrixOrder
{
	MATRIXORDER_COLUMN_MAJOR = 0,
	MATRIXORDER_ROW_MAJOR,

	MATRIXORDER_LAST
};

const char* getMatrixOrderName (MatrixOrder qualifier);

// Declaration utilities.

struct Layout
{
					Layout			(int location_ = -1, int binding_ = -1, int offset_ = -1, FormatLayout format_ = FORMATLAYOUT_LAST, MatrixOrder matrixOrder_ = MATRIXORDER_LAST);

	bool			operator==		(const Layout& other) const;
	bool			operator!=		(const Layout& other) const;

	int				location;
	int				binding;
	int				offset;
	FormatLayout	format;
	MatrixOrder		matrixOrder;
} DE_WARN_UNUSED_TYPE;

struct VariableDeclaration
{
						VariableDeclaration	(const VarType& varType_, const std::string& name_, Storage storage_ = STORAGE_LAST, Interpolation interpolation_ = INTERPOLATION_LAST, const Layout& layout_ = Layout(), deUint32 memoryAccessQualifierBits_ = 0);

	bool				operator==			(const VariableDeclaration& other) const;
	bool				operator!=			(const VariableDeclaration& other) const;

	Layout				layout;
	Interpolation		interpolation;
	Storage				storage;
	VarType				varType;
	deUint32			memoryAccessQualifierBits;
	std::string			name;
} DE_WARN_UNUSED_TYPE;

struct InterfaceBlock
{
											InterfaceBlock	(void);

	glu::Layout								layout;
	Storage									storage;
	int										memoryAccessQualifierFlags;
	std::string								interfaceName;
	std::string								instanceName;
	std::vector<glu::VariableDeclaration>	variables;
	std::vector<int>						dimensions;
} DE_WARN_UNUSED_TYPE;

//! Internals for declare() utilities.
namespace decl
{

struct Indent
{
	int level;
	Indent (int level_) : level(level_) {}
};

struct DeclareStructTypePtr
{
	DeclareStructTypePtr (const StructType* structPtr_, int indentLevel_) : structPtr(structPtr_), indentLevel(indentLevel_) {}

	const StructType*	structPtr;
	int					indentLevel;
};

struct DeclareStructType
{
	DeclareStructType (const StructType& structType_, int indentLevel_) : structType(structType_), indentLevel(indentLevel_) {}

	StructType			structType;
	int					indentLevel;
};

struct DeclareVariable
{
	DeclareVariable (const VarType& varType_, const std::string& name_, int indentLevel_) : varType(varType_), name(name_), indentLevel(indentLevel_) {}

	VarType				varType;
	std::string			name;
	int					indentLevel;
};

std::ostream&		operator<<		(std::ostream& str, const Indent&				indent);
std::ostream&		operator<<		(std::ostream& str, const DeclareStructTypePtr&	decl);
std::ostream&		operator<<		(std::ostream& str, const DeclareStructType&	decl);
std::ostream&		operator<<		(std::ostream& str, const DeclareVariable&		decl);

} // decl

inline decl::Indent					indent			(int indentLevel)														{ return decl::Indent(indentLevel);									}
inline decl::DeclareStructTypePtr	declare			(const StructType*	structPtr,	int indentLevel = 0)					{ return decl::DeclareStructTypePtr	(structPtr,		indentLevel);	}
inline decl::DeclareStructType		declare			(const StructType&	structType,	int indentLevel = 0)					{ return decl::DeclareStructType	(structType,	indentLevel);	}
inline decl::DeclareVariable		declare			(const VarType& varType, const std::string& name, int indentLevel = 0)	{ return decl::DeclareVariable		(varType, name, indentLevel);	}

std::ostream&						operator<<		(std::ostream& str, const Layout& decl);
std::ostream&						operator<<		(std::ostream& str, const VariableDeclaration& decl);

} // glu

#endif // _GLUVARTYPE_HPP
